﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace main.sdk.utils
{
    public class Counter
    {

        #region Variables

        private bool   m_Growing = true;  //!< Está indo para o final.
        private bool   m_IsLoop = false;  //!< É um contador em loop?
        private int    m_Smoothness = 0;  //!< Suavidade.
        private double m_From    = 0;     //!< Valor inicial.
        private double m_To      = 0;     //!< Valor final.
        private double m_Time = 0;        //!< Tempo para percorrer.
        private double m_Current = 0;     //!< Valor atual (0 a 1)

        #endregion

        #region Constructors

        /**
         * Construtor do contador. Vazio
         */
        public Counter()
        {
            m_From = 0;
            m_To   = 0;
            m_Time = 1;
            m_Smoothness = 0;
            m_IsLoop = false;
            m_Current = 0;
        }
        /**
         * Construtor do contador.
         * 
         * @param From - Valor inicial
         * @param To - Valor final
         * @param Time - Tempo necessário para ir de inicial até final
         * @param Smoothness - Suavidade. (0 - Linear, 1 - Sai devagar, anda rápido, chega devagar, 2 - Sai rápido, chega devagar, 3 - Sai devagar, chega rápido)
         */
        public Counter(double From, double To, double Time, int Smoothness = 0, bool Loop = false)
        {
            m_From = From;
            m_To = To;
            m_Time = Time;
            m_Smoothness = Smoothness;
            m_IsLoop = Loop;
            m_Current = 0;
        }

        #endregion

        #region Members
        /**
         * Acessa o valor do contador.
         */
        public double Value
        {
            /* Pega o valor atual do contador */
            get
            {
                /* Dependendo da suavidade Retorna um valor */
                if (m_Smoothness == 1)
                    return m_From + (m_To - m_From) * (1 - Math.Cos(m_Current * Math.PI)) / 2.0;
                else if (m_Smoothness == 2)
                    return m_From + (m_To - m_From) * Math.Sin(m_Current * Math.PI / 2);
                else if (m_Smoothness == 3)
                    return m_From + (m_To - m_From) * (1 - Math.Cos(m_Current * Math.PI / 2));
                else
                    return m_From + (m_To - m_From) * m_Current;
            }
            /* Define o valor do contador */
            set
            {
                /* Verifica os limites */
                bool start  = false;
                bool finish = false;
                if (m_From > m_To)
                {
                    if (value >= m_From)
                        start = true;
                    else if (value <= m_To)
                        finish = true;
                }
                else
                {
                    if (value <= m_From)
                        start = true;
                    else if (value >= m_To)
                        finish = true;
                }

                /* Se é um valor fora do limite inicial */
                if (start)
                {
                    m_Current = 0;
                    m_Growing = true;
                }
                /* Se é um valor fora do limite final */
                else if (finish)
                {
                    m_Current = 1;
                    m_Growing = false;
                }
                /* Se é um valor dentro dos limites */
                else
                {
                    /* Um valor temporário para os cálculos.  */
                    double temp = (value - m_From) / (m_To - m_From);

                    /* Dependendo da suavidade, é um cálculo diferente. */
                    if (m_Smoothness == 1)
                        m_Current = Math.Acos(1 - temp * 2.0) / (Math.PI);
                    else if (m_Smoothness == 2)
                        m_Current = Math.Asin(temp) * 2 / Math.PI;
                    else if (m_Smoothness == 3)
                        m_Current = Math.Acos(1 - temp) * 2 / Math.PI;
                    else
                        m_Current = temp;
                }
            }
        }

        /**
         * Atualiza o contador.
         * 
         * @param Times - Tempo que passou.
         */
        public void Update(double Time)
        {
            /* Verifica se o tempo é 0 */
            if (Math.Abs(m_Time) <= 0.0000001)
            {
                m_Current = 1.0;
                return;
            }

            /* Verifica se é Loop. */
            if (m_IsLoop)
            {
                /* Voltas completas */
                while (Time > (m_Time * 2))
                    Time -= m_Time * 2;

                /* Meias voltas */
                if (Time >= m_Time)
                {
                    m_Growing = (!m_Growing);
                    Time -= m_Time;
                }

                /* Normaliza o tempo que passou */
                double step = Time / m_Time;
                while (step > 0.00000000001)
                {
                    if (m_Growing)
                    {
                        double temp = m_Current + step;
                        if (temp > 1)
                        {
                            m_Current = 1;
                            step = temp - 1;
                            m_Growing = false;
                        }
                        else
                        {
                            m_Current = temp;
                            step = 0;
                        }
                    }
                    else
                    {
                        double temp = m_Current - step;
                        if (temp < 0)
                        {
                            m_Current = 0;
                            step = -temp;
                            m_Growing = true;
                        }
                        else
                        {
                            m_Current = temp;
                            step = 0;
                        }
                    }
                }
            }
            /* Not a Loop */
            else
            {
                double step = Time / m_Time;
                double temp = m_Current + step;
                if (temp >= 1)
                    m_Current = 1;
                else
                    m_Current = temp;
            }
        }

        /**
         * Verifica se finalizou o contador.
         */
        public bool Finished()
        {
            /* Verifica se é Loop. */
            if (m_IsLoop)
                return false;
            return (m_Current >= 1);
        }

        /**
         * Verifica se Esta ativo.
         */
        public bool Active()
        {
            return !Finished();
        }

        /**
         * Reseta o contador. Vazio
         */
        public void Reset()
        {
            /* Zera o contador. */
            m_Current = 0;
            m_IsLoop = false;
        }

        /**
         * Reseta o contador.
         * 
         * @param From - Valor inicial
         * @param To - Valor final
         * @param Time - Tempo necessário para ir de inicial até final
         * @param Smoothness - Suavidade. (0 - Linear, 1 - Sai devagar, anda rápido, chega devagar, 2 - Sai rápido, chega devagar, 3 - Sai devagar, chega rápido)
         */
        public void Reset(double From, double To, double Time, int Smoothness = 0, bool Loop = false)
        {
            /* Zera o contador. */
            m_From       = From;
            m_To         = To;
            m_Time       = Time;
            m_Smoothness = Smoothness;
            m_IsLoop     = Loop;
            m_Current    = 0;
        }

        #endregion
    }
}
